package org.dna.opdracht4;

import java.util.Collections;
import java.util.List;
import java.util.ArrayList;

public class PERTNetwork<T> extends GenericGraph<T> {
	public PERTNetwork() {

	}

	/**
	 * Sorteerd het huidige Pert Netwerk op topologische volgorde en zet de
	 * "leastTime" voor elke vertex waarna het de topologisch gesorteerde lijst
	 * met vertex's terug geeft.
	 * 
	 * @return List<Vertex<T>> de op topologisch gesorteerde lijst
	 * */
	public List<Vertex<T>> topologicalSort() {
		List<Vertex<T>> topologicalSorted = new ArrayList<Vertex<T>>();
		List<Vertex<T>> temp = getStartVertices();
		while (!temp.isEmpty()) {
			Vertex<T> remove = temp.get(0);
			// test prints kunnen weg
			// vergelijk of isVisited (int) gelijk is of groter dan incomming
			// edges
			// (betekend: heeft "geen" incoming edges meer)
			if (remove.getVisited() >= remove.getInEdges().size()) {
				if (!topologicalSorted.contains(remove)) {
					topologicalSorted.add(remove);
				}
				temp.remove(remove);
				for (Edge<T> edge : remove.getOutEdges()) {
					edge.getTo().setEarliestTime(
							remove.getEarliestTime() + edge.getCost());
					// visited++
					edge.getTo().increaseVisited();

					if (!temp.contains(edge.getTo())) {
						temp.add(edge.getTo());
					}
				}
			} else {
				// als er nog wel incoming edges zijn, zet de vertex
				// achter aan de lijst.
				temp.remove(remove);
				temp.add(temp.size(), remove);
			}
		}
		// reset visited
		resetVisited(topologicalSorted);
		return topologicalSorted;
	}

	/**
	 * Voert een reverse topologische sort uit en zet de "latestTime" per vertex
	 * waarna het de reverse topologische gesorteerde lijst terug geeft.
	 * 
	 * @param List
	 *            <Vertex<T>> een topologisch gesorteerde lijst.
	 * @return List<Vertex<T>> een reversed topologisch gesorteerde lijst
	 * */
	public List<Vertex<T>> topologicalSortReversed(List<Vertex<T>> list) {
		List<Vertex<T>> topologicalReversed = list;
		Collections.reverse(topologicalReversed);
		// loop de nodes in reverse order door
		for (Vertex<T> v : topologicalReversed) {
			for (Edge<T> edge : v.getInEdges()) {
				if (v.getOutEdges().size() == 0) {
					v.setLatestTime(v.getEarliestTime());
					v.increaseVisited();
				}

				edge.getFrom()
						.setLatestTime(v.getLatestTime() - edge.getCost());
				edge.getFrom().increaseVisited();
			}
		}
		resetVisited(topologicalReversed);
		return topologicalReversed;
	}

	private void resetVisited(List<Vertex<T>> list) {
		for (Vertex<T> v : list) {
			v.resetVisited();
		}
	}

	private List<Vertex<T>> getStartVertices() {
		List<Vertex<T>> vertices = getVertices();
		List<Vertex<T>> toRemove = new ArrayList<Vertex<T>>();

		for (Vertex<T> vertex : vertices) {
			for (Edge<T> edge : vertex.getInEdges()) {
				if (vertices.contains(edge.getTo())) {
					toRemove.add(edge.getTo());
				}
			}
		}
		vertices.removeAll(toRemove);

		return vertices;
	}
}
